﻿
"use strict";

//top wrapper to prevent multiple inclusion
(function () {
    if (jQuery && jQuery.jstree && jQuery.sptree) {
        return;
    }

    (function ($) {

        var defaults = {
            'showwebs': true,
            'rootweburl': '', //Web url. If empty - current web (hostWeb for app).
            'showlists': true,
            'filterlistsByTemplate': null, //empty string(or null) or templateId
            'filterlistsByCT': '', //empty string(or null) or CT name
            'useInSPApp': false,
            'selectable': 'none', //lists, webs, all, none
            'checkable': 'none', //lists, webs, all, none
            'links': true
        };

        var methods = {
            init: function (params) {

                var options = $.extend({}, defaults, params.sptree);

                return this.each(function () {
                    var $this = $(this),
                        treedata = $this.data('sptree');

                    // If plugin has not been initialized yet
                    if (!treedata) {
                        $(this).data('sptree', {
                            target: $this
                        });
                        
                        options.rootweburl = options.rootweburl || (options.useInSPApp ? decodeURIComponent(getQueryStringParameter('SPHostUrl')) : window._spPageContextInfo.webAbsoluteUrl);

                        if (options.checkable !== enums.checkable.none) {
                            addCss('li.sptree-uncheckable > a > i.jstree-checkbox { display: none !important; }');
                            params.plugins ? ($.inArray("checkbox", params.plugins) || params.plugins.push("checkbox")) : params.plugins = ["checkbox"];
                        }

                        if (!params.core) {
                            params.core = {};
                        }
                        params.core.data = function (parentNode, cb) {
                            var ajaxOptions = {
                                url: (function () {
                                    var url,
                                    target = (parentNode.data && parentNode.data.url) || options.rootweburl,
                                    parameters = [],
                                    expands = [];

                                    if (options.useInSPApp) {
                                        var appweburl = decodeURIComponent(getQueryStringParameter('SPAppWebUrl'));
                                        url = appweburl + "/_api/SP.AppContextSite(@target)/web";
                                        parameters.push("@target='" + target + "'");
                                    } else {
                                        url = target + "/_api/web";
                                    }

                                    if (options.showwebs) {
                                        expands.push('WebInfos');
                                    }
                                    if (options.showlists) {
                                        expands.push('Lists/DefaultView');
                                    }
                                    if (options.filterlistsByCT) {
                                        expands.push('Lists/ContentTypes');
                                    }

                                    if (expands.length > 0) parameters.push("$expand=" + expands.join(","));

                                    if (parameters.length > 0) url += "?" + parameters.join("&");

                                    return url;
                                })(),
                                success: function (sourcedata) {
                                    var jsonData = sourcedata.d;
                                    var nodes = [];

                                    if (options.showwebs) {
                                        var webs = jsonData.WebInfos.results;
                                        for (var i = 0; i < webs.length; i++) {
                                            var web = webs[i];
                                            var node = spNodes.createNode(web, options);
                                            if (node.included) {
                                                nodes.push(node);
                                            }
                                        }
                                    }

                                    if (options.showlists) {
                                        var lists = jsonData.Lists.results;
                                        for (var i = 0; i < lists.length; i++) {
                                            var list = lists[i];
                                            var node = spNodes.createNode(list, options);
                                            if (options.filterlistsByTemplate) {
                                                node.included = list.BaseTemplate == options.filterlistsByTemplate;
                                            }
                                            if (options.filterlistsByCT && list.ContentTypes.results) {
                                                var ctMatch = $.grep(list.ContentTypes.results, function (e) { return e.Name == options.filterlistsByCT; });
                                                node.included = node.included && ctMatch.length > 0;
                                            }
                                            if (node.included) {
                                                nodes.push(node);
                                            }
                                        }
                                    }

                                    if (options.showwebs) {
                                        if (parentNode.id == "#") {
                                            var rootNode = spNodes.createNode(jsonData, options);
                                            rootNode.children = nodes;
                                            cb.call(this, [rootNode]);
                                            return;
                                        }
                                    }

                                    cb.call(this, nodes);
                                    return;
                                },
                                error: function (e) {
                                    var message = e.status + ' ' + e.statusText;
                                    if (e.responseText) {
                                        try {
                                            var obj = JSON.parse(e.responseText);
                                            message += '\n' + obj.error.message.value;
                                        } catch (ex) {
                                            message += e.responseText;
                                        }
                                    }
                                    alert("Error:\n" + message);
                                },
                                headers: {
                                    "Accept": "application/json; odata=verbose"
                                }
                            };
                            $.ajax(ajaxOptions);
                        };

                        $this.jstree(params);
                    }
                });
            }
        };

        $.fn.sptree = function (method) {
            if (methods[method]) {
                return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
            } else if (typeof method === 'object' || !method) {
                return methods.init.apply(this, arguments);
            } else {
                $.error('There is no method with name ' + method + ' for jquery.jstree.sptree');
                return null;
            }
        };

        function getQueryStringParameter(param) {
            var params = document.URL.split("?")[1].split("&");
            for (var j = 0; j < params.length; j = j + 1) {
                var singleParam = params[j].split("=");
                if (singleParam[0] == param) {
                    return singleParam[1];
                }
            }
            return null;
        }

        function getHostname(urlStr) {
            var a = document.createElement('a');
            a.href = urlStr;
            return a.hostname;
        }
        
        function addCss(css) {
            var head = document.head || document.getElementsByTagName('head')[0],
                style = document.createElement('style');

            style.type = 'text/css';
            if (style.styleSheet) {
                style.styleSheet.cssText = css;
            } else {
                style.appendChild(document.createTextNode(css));
            }

            head.appendChild(style);
        }
        
        var spNodes = {
            "defaultType": {
                title: function (jsonData) {
                    return jsonData.Title ? jsonData.Title : jsonData.Name ? jsonData.Name : "Untitled";
                }
            },
            "SP.Web": {
                icon: "SharePointFoundation16.png",
                JSOMtype: "SP.Web",
                title: function (jsonData) {
                    return jsonData.Title;
                },
                url: function (jsonData) {
                    return jsonData.Url;
                },
                selectable: function (options) {
                    if (options.selectable.toLowerCase && (options.selectable.toLowerCase() === enums.selectable.all || options.selectable.toLowerCase() === enums.selectable.webs)) {
                        return true;
                    } else {
                        return false;
                    }
                },
                checkable: function (options) {
                    if (options.checkable.toLowerCase && (options.checkable.toLowerCase() === enums.checkable.all || options.checkable.toLowerCase() === enums.checkable.webs)) {
                        return true;
                    } else {
                        return false;
                    }
                },
                state: function (options) {
                    return {
                        "opened": true,
                        "disabled": !this.selectable(options) && !this.checkable(options)
                    };
                }
            },
            "SP.List": {
                icon: function (jsonData) {
                    return jsonData.ImageUrl;
                },
                JSOMtype: "SP.List",
                title: function (jsonData) {
                    return jsonData.Title;
                },
                included: function (jsonData) {
                    return jsonData.Hidden === false;
                },
                url: function (jsonData) {
                    if (jsonData.DefaultView.ServerRelativeUrl)
                        return jsonData.DefaultView.ServerRelativeUrl;
                    return undefined;
                },
                selectable: function (options) {
                    if (options.selectable.toLowerCase && (options.selectable.toLowerCase() == enums.selectable.all || options.selectable.toLowerCase() === enums.selectable.lists)) {
                        return true;
                    } else {
                        return false;
                    }
                },
                checkable: function (options) {
                    if (options.checkable.toLowerCase && (options.checkable.toLowerCase() == enums.checkable.all || options.checkable.toLowerCase() === enums.checkable.lists)) {
                        return true;
                    } else {
                        return false;
                    }
                },
                state: function (options) {
                    return {
                        "opened": false,
                        "disabled": !this.selectable(options) && !this.checkable(options)
                    };
                },
                children: false
            },
            "SP.WebInformation": {
                icon: "SharePointFoundation16.png",
                JSOMtype: "SP.Web",
                included: function (jsonData) {
                    return jsonData.WebTemplate.toLowerCase() != "app";
                },
                url: function (jsonData, rootWebUrl) {
                    return window.location.protocol + '//' + getHostname(rootWebUrl) + jsonData.ServerRelativeUrl;
                },
                selectable: function (options) {
                    if (options.selectable.toLowerCase && (options.selectable.toLowerCase() === enums.selectable.all || options.selectable.toLowerCase() === enums.selectable.webs)) {
                        return true;
                    } else {
                        return false;
                    }
                },
                checkable: function (options) {
                    if (options.checkable.toLowerCase && (options.checkable.toLowerCase() === enums.checkable.all || options.checkable.toLowerCase() === enums.checkable.webs)) {
                        return true;
                    } else {
                        return false;
                    }
                },
                state: function (options) {
                    return {
                        "opened": false,
                        "disabled": !this.selectable(options) && !this.checkable(options)
                    };
                },
                children: true
            },
            getType: function (jsonData, options) {
                var typeName;
                if (jsonData.__metadata) {
                    typeName = jsonData.__metadata.type;
                } else {
                    var typeNameUri = jsonData.__deferred.uri;
                    var typeNameIndex = typeNameUri.lastIndexOf('/');
                    typeName = typeNameUri.substring(typeNameIndex + 1, typeNameUri.lenght);
                }
                return this.formatType(jsonData, typeName, options);
            },
            formatType: function (jsonData, typeName, options) {
                var typeSettins = this[typeName];
                var title = typeof typeSettins.title == 'function' ? typeSettins.title(jsonData) : typeSettins.title;

                return {
                    typename: typeName,
                    icon: typeof typeSettins.icon == 'function' ? typeSettins.icon(jsonData) : typeSettins.icon ? "/_layouts/15/images/" + typeSettins.icon : "/_layouts/15/images/BULLET.GIF",
                    title: title ? title : this.defaultType.title(jsonData),
                    included: typeof typeSettins.included == 'function' ? typeSettins.included(jsonData) : typeSettins.included ? typeSettins.included : true,
                    JSOMtype: typeSettins.JSOMtype ? typeSettins.JSOMtype : typeName,
                    state: typeSettins.state ? typeSettins.state(options) : {"opened": false},
                    url: typeSettins.url ? typeSettins.url(jsonData, options.rootweburl) : undefined,
                    selectable: typeSettins.selectable ? typeSettins.selectable(options) : false,
                    checkable: typeSettins.checkable ? typeSettins.checkable(options) : false,
                    children: typeSettins.children == true
                };
            },
            createNode: function (jsonData, options) {
                var nodeType = spNodes.getType(jsonData, options);
                var node = {
                    "text": nodeType.title,
                    "icon": nodeType.icon,
                    "children": nodeType.children,
                    "a_attr": {
                        'title': nodeType.JSOMtype,
                        'href': options.links ? nodeType.url : "#"
                    },
                    "li_attr": {
                        'class': nodeType.checkable ? "" : "sptree-uncheckable"
                    },
                    state: nodeType.state,
                    included: nodeType.included,
                    data: {
                        url: nodeType.url,
                        type: nodeType.JSOMtype                        
                    }
                };
                return node;
            }
        };
        
        var enums = {
            selectable: {
                lists: 'lists',
                webs: 'webs',
                all: 'all',
                none: 'none'
            },
            checkable: {
                lists: 'lists',
                webs: 'webs',
                all: 'all',
                none: 'none'
            }
        };
    })(jQuery);
})();